package com.study.log.bank;

import com.study.util.ClassUtil;
import com.study.util.FileUtil;
import com.study.log.ClassInfo;
import com.study.log.MethodInfo;
import de.hunsicker.jalopy.Jalopy;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @version V1.0
 * @title: AddLogUtil
 * @description: ${DESCRIPTION}
 * @author: wanghuilong
 * @date: 2019-10-19 16:43
 * @copyright: http://fingo.com.my/
 */
public class AddLogUtil {

    public static String rootPath = "/Users/fingo/Desktop";

    public static String classPath = "target/classes";

    //src/main/java
    public static String srcPath = "";

    public static void main(String[] args) {
        String javaFilePath = "/Users/fingo/Desktop/work_dir/workspace/study/design-patterns/src/main/java/com/study/log/PersonServiceImpl.java";

        //String javaFilePath = "/Users/fingo/Desktop/work_dir/workspace/study/design-patterns/src/main/java/com/study/log/AddLogUtil.java";
        try {
            String javaSource = FileUtil.read(javaFilePath);

            FileUtil.writeToFileOverWrite(javaSource, javaFilePath + ".bank");

            String formatSource = formatSource(javaSource);

            System.out.println(formatSource);

            String[] lineList = formatSource.split("\n");

            List<String> list = handleImpoertInfo(Arrays.asList(lineList));

            list = handleAnnotationsInfo(list);


            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }

            List<ClassInfo> classInfos = handleClassInfoList(list);

            handleClassMethodInfo(classInfos);

            list = assembly(list, classInfos);

            //list = handleMethodInfo(list);


            System.out.println("---------------add log -------------");

            //FileUtil.writeToFileByLine(result,javaFilePath);
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                stringBuilder.append(list.get(i)).append("\n");
            }

            System.out.println(stringBuilder);

            String s = formatSource(stringBuilder.toString());

            System.out.println(s);

            FileUtil.writeToFileOverWrite(s, javaFilePath);

            ClassUtil classUtil = new ClassUtil(rootPath, classPath, srcPath);

            classUtil.doJavac(javaFilePath);
        } catch (Exception e) {
            String javaSource = FileUtil.read(javaFilePath + ".bank");
            FileUtil.writeToFileOverWrite(javaSource, javaFilePath);
        } finally {
            File file = new File(javaFilePath + ".bank");
            file.delete();
        }

    }

    private static List<String> assembly(List<String> list, List<ClassInfo> classInfos) {
        List<String> result = new ArrayList<>();
        all:
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            Integer index = assemblyClassInfo(i, classInfos, result);
            if (0 == index) {
                result.add(line);
            } else {
                i = index;
            }
        }
        return result;
    }

    private static Integer assemblyClassInfo(int i, List<ClassInfo> classInfos, List<String> result) {
        if (null == classInfos || classInfos.size() == 0) {
            return 0;
        }
        Integer index = 0;
        for (int j = 0; j < classInfos.size(); j++) {
            ClassInfo classInfo = classInfos.get(j);
            List<ClassInfo> subClass = classInfo.getSubClass();
            List<String> classSourceLine = classInfo.getSourceLine();
            List<MethodInfo> methodInfoList = classInfo.getList();
            if (i == classInfo.getStartIndex()) {
                for (int k = 0; k < classSourceLine.size(); k++) {
                    String line = classSourceLine.get(k);
                    Integer n = assemblyClassInfo(i + k, subClass, result);
                    if (0 == n) {
                        n = assemblyMethodInfo(i + k, methodInfoList, result);
                    }
                    if (0 == n) {
                        result.add(line);
                    } else {
                        k = n - i;
                    }
                }
                index = classInfo.getEndIndex();
            }
        }
        return index;
    }

    private static Integer assemblyMethodInfo(int i, List<MethodInfo> methodInfoList, List<String> result) {
        if (null == methodInfoList || methodInfoList.size() == 0) {
            return 0;
        }
        Integer index = 0;
        for (int j = 0; j < methodInfoList.size(); j++) {
            MethodInfo methodInfo = methodInfoList.get(j);
            if (i == methodInfo.getStartIndex()) {
                result.addAll(methodInfo.getSourceLine());
                index = methodInfo.getEndIndex();
            }
        }
        return index;
    }

    private static void handleClassMethodInfo(List<ClassInfo> classInfos) {
        for (int i = 0; i < classInfos.size(); i++) {
            ClassInfo classInfo = classInfos.get(i);
            coverMethodInfo(classInfo);
            List<ClassInfo> subClass = classInfo.getSubClass();
            handleClassMethodInfo(subClass);
        }
    }

    private static List<MethodInfo> coverMethodInfo(ClassInfo classInfo) {
        List<String> sourceLine = classInfo.getSourceLine();
        List<MethodInfo> methodInfoList = new ArrayList<>();
        List<String> list = new ArrayList<>();
        list.addAll(sourceLine);
        Integer startIndex = classInfo.getStartIndex();

        for (int i = 0; i < sourceLine.size(); i++) {
            String line = sourceLine.get(i);
            if (!StringUtils.isBlank(line) && line.indexOf(" class ") == -1 && !checkInSubClass(classInfo, startIndex + i)) {
                Integer subStartIndex = startIndex + i;
                List<String> subSource = new ArrayList<>();
                Integer index = 0;
                sub:
                for (int j = i; j < sourceLine.size(); j++) {
                    line = sourceLine.get(j);
                    if (line.indexOf("{") > -1) {
                        int n = (line.split("\\{").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index += n;
                    }
                    if (line.indexOf("}") > -1) {
                        int n = (line.split("\\}").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index -= n;
                    }
                    subSource.add(line);
                    if (index == 0 && line.indexOf("}") > -1) {
                        i = j;
                        break sub;
                    }
                }
                MethodInfo methodInfo = new MethodInfo();
                methodInfo.setEndIndex(startIndex + i);
                methodInfo.setStartIndex(subStartIndex);
                methodInfo.setSourceLine(subSource);
                methodInfoList.add(methodInfo);
                continue;
            }
        }

        for (int i = 0; i < methodInfoList.size(); i++) {
            MethodInfo methodInfo = methodInfoList.get(i);
            StringBuilder stringBuilder = new StringBuilder();
            removeLogCode(methodInfo);
            List<String> lines = methodInfo.getSourceLine();
            for (int j = 0; j < lines.size(); j++) {
                String line = lines.get(j);
                stringBuilder.append(line.trim());
                String methodStart = getMethodStart(line.trim());
                if (!methodStart.equals("") && null == methodInfo.getMethodName()) {
                    //获取方法名
                    String methodName = getMethodName(methodStart);
                    methodInfo.setMethodName(methodName);
                    //获取输入参数，进行打印
                    List<String> methodParam = getMethodParam(methodStart);
                    methodInfo.setParamNames(methodParam);
                    addLogCode(methodInfo, j);
                }
                methodStart = getMethodStart(stringBuilder.toString());
                if (null == methodInfo.getMethodName() && !methodStart.equals("")) {
                    //获取方法名
                    String methodName = getMethodName(methodStart);
                    methodInfo.setMethodName(methodName);
                    //获取输入参数，进行打印
                    List<String> methodParam = getMethodParam(methodStart);
                    methodInfo.setParamNames(methodParam);
                    addLogCode(methodInfo, j);
                }
            }
            if (null == methodInfo.getMethodName()) {
                methodInfoList.remove(methodInfo);
            }
        }
        classInfo.setList(methodInfoList);
        return methodInfoList;
    }

    private static void removeLogCode(MethodInfo methodInfo) {

        StringBuilder stringBuilder = new StringBuilder();
        List<Integer> removerIndex = new ArrayList<>();
        List<String> sourceLine = methodInfo.getSourceLine();
        for (int i = 0; i < sourceLine.size(); i++) {
            String s = sourceLine.get(i);
            if (!StringUtils.isBlank(s) &&
                    (s.indexOf("addLogStart") > -1 || stringBuilder.toString().indexOf("addLogStart") > -1)) {
                stringBuilder.append(s.trim());
                if (s.indexOf("addLogStart") > -1) {
                    removerIndex.add(i);
                } else if (stringBuilder.toString().indexOf("addLogStart") > -1) {
                    removerIndex.add(i);
                }
                if (stringBuilder.toString().trim().endsWith(";")) {
                    stringBuilder = new StringBuilder();
                }
            }
        }
        List<String> sourceNewList = new ArrayList<>();
        all:
        for (int i = 0; i < sourceLine.size(); i++) {
            for (int j = 0; j < removerIndex.size(); j++) {
                if (i == removerIndex.get(j)) {
                    continue all;
                }
            }
            sourceNewList.add(sourceLine.get(i));
        }

        methodInfo.setSourceLine(sourceNewList);
    }

    /**
     * 校验是否在子类中
     *
     * @param classInfo
     * @param i
     * @return
     */
    private static boolean checkInSubClass(ClassInfo classInfo, int i) {
        List<ClassInfo> subClass = classInfo.getSubClass();
        if (null == subClass || subClass.size() == 0) {
            return false;
        }
        for (int j = 0; j < subClass.size(); j++) {
            ClassInfo subClassInfo = subClass.get(j);
            if (i >= subClassInfo.getStartIndex() && i <= subClassInfo.getEndIndex()) {
                return true;
            }
        }
        return false;
    }

    private static void addLogCode(MethodInfo methodInfo, int j) {
        List<String> sourceLine = methodInfo.getSourceLine();
        List<String> sourceNewList = new ArrayList<>();
        StringBuilder logLine = createInputParamLog(methodInfo.getMethodName(), methodInfo.getParamNames());
        for (int i = 0; i < sourceLine.size(); i++) {
            String str = sourceLine.get(i);
            if (i == j) {
                sourceNewList.add(str);
                sourceNewList.add(logLine.toString());
                continue;
            }
            sourceNewList.add(str);
        }


        methodInfo.setSourceLine(sourceNewList);
    }


    private static List<ClassInfo> handleClassInfoList(List<String> list) {
        List<ClassInfo> classInfos = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            if (!StringUtils.isBlank(line) && line.indexOf(" class ") > -1) {
                Integer startIndex = i;
                List<String> subSource = new ArrayList<>();
                Integer index = 0;
                sub:
                for (int j = i; j < list.size(); j++) {
                    line = list.get(j);
                    if (line.indexOf("{") > -1) {
                        int n = (line.split("\\{").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index += n;
                    }
                    if (line.indexOf("}") > -1) {
                        int n = (line.split("\\}").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index -= n;
                    }
                    subSource.add(line);
                    if (index == 0) {
                        i = j;
                        break sub;
                    }
                }
                ClassInfo classInfoList = handleClassInfo(subSource, startIndex, i);
                classInfos.add(classInfoList);
                continue;
            }
        }
        return classInfos;
    }

    private static ClassInfo handleClassInfo(List<String> list, Integer startIndex, Integer endIndex) {
        List<ClassInfo> classInfos = new ArrayList<>();
        ClassInfo classInfo = new ClassInfo();
        classInfo.setStartIndex(startIndex);
        classInfo.setEndIndex(endIndex);
        classInfo.setSourceLine(list);
        classInfo.setSubClass(classInfos);
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            if (!StringUtils.isBlank(line) && line.indexOf(" class ") > -1 && i > 0) {
                Integer subStartIndex = startIndex + i;
                List<String> subSource = new ArrayList<>();
                Integer index = 0;
                sub:
                for (int j = i; j < list.size(); j++) {
                    line = list.get(j);
                    if (line.indexOf("{") > -1) {
                        int n = (line.split("\\{").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index += n;
                    }
                    if (line.indexOf("}") > -1) {
                        int n = (line.split("\\}").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index -= n;
                    }
                    subSource.add(line);
                    if (index == 0) {
                        i = j;
                        break sub;
                    }
                }
                ClassInfo subClassInfo = handleClassInfo(subSource, subStartIndex, startIndex + i);
                classInfos.add(subClassInfo);
                continue;
            }
        }
        return classInfo;
    }


    private static List<String> handleMethodInfo(List<String> list) {
        List<String> result = new ArrayList<>();
        List<MethodInfo> methodInfoList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            String methodStart = getMethodStart(line);
            if (!methodStart.equals("")) {
                MethodInfo methodInfo = new MethodInfo();
                //获取方法名
                String methodName = getMethodName(methodStart);
                methodInfo.setMethodName(methodName);
                //获取输入参数，进行打印
                List<String> methodParam = getMethodParam(methodStart);
                methodInfo.setParamNames(methodParam);
                Integer index = 0;
                methodInfo.setStartIndex(i);
                sub:
                for (int j = i; j < list.size(); j++) {
                    line = list.get(j);
                    if (line.indexOf("{") > -1) {
                        int n = (line.split("\\{").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index += n;
                    }
                    if (line.indexOf("}") > -1) {
                        int n = (line.split("\\}").length - 1);
                        if (n <= 0) {
                            n = 1;
                        }
                        index -= n;
                    }
                    methodInfo.getSourceLine().add(line);
                    if (index == 0) {
                        methodInfoList.add(methodInfo);
                        i = j;
                        break sub;
                    }
                }
                methodInfo.setEndIndex(i);
                continue;
            }
        }

        all:
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            for (int k = 0; k < methodInfoList.size(); k++) {
                MethodInfo methodInfo = methodInfoList.get(k);
                if (i == methodInfo.getStartIndex()) {
                    StringBuilder logLine = createInputParamLog(methodInfo.getMethodName(), methodInfo.getParamNames());
                    List<String> sourceLine = methodInfo.getSourceLine();
                    List<String> sourceNewList = new ArrayList<>();
                    for (int j = 0; j < sourceLine.size(); j++) {
                        String s = sourceLine.get(j);
                        if (!StringUtils.isBlank(s) && s.indexOf("addLogStart") > -1) {
                            sourceLine.remove(s);
                        }
                    }

                    for (int j = 0; j < sourceLine.size(); j++) {
                        String str = sourceLine.get(j);
                        String methodStart = getMethodStart(str);
                        if (!methodStart.equals("")) {
                            sourceNewList.add(str);
                            sourceNewList.add(logLine.toString());
                            continue;
                        }
                        sourceNewList.add(str);
                    }
                    result.addAll(sourceNewList);
                    i = methodInfo.getEndIndex();
                    continue all;
                }
            }
            result.add(line);
        }


        //判断是否是无返回方法
        //获取return行;，进行响应打印

        return result;
    }

    private static StringBuilder createInputParamLog(String methodName, List<String> methodParam) {
        StringBuilder logLine = new StringBuilder("log.info(\"addLogStart ").append(methodName).append(" InputParam: ");
        for (int k = 0; k < methodParam.size(); k++) {
            logLine.append(methodParam.get(k)).append(" : {} ");
        }
        logLine.append("\"");
        for (int k = 0; k < methodParam.size(); k++) {
            logLine.append(",").append(methodParam.get(k));
        }
        logLine.append(");");
        return logLine;
    }

    /**
     * 处理注解
     *
     * @param sourceList
     * @return
     */
    private static List<String> handleAnnotationsInfo(List<String> sourceList) {
        List<String> result = new ArrayList<>();
        List<String> annotationsLine = new ArrayList<>();
        Boolean addImport = true;
        int end = 0;
        //查出所有的导包代码
        for (int i = 0; i < sourceList.size(); i++) {
            String line = sourceList.get(i);
            if (line.startsWith("@")) {
                annotationsLine.add(line);
                end = i;
                continue;
            }
        }

        //判断是否已经导入包
        for (int i = 0; i < annotationsLine.size(); i++) {
            String s = annotationsLine.get(i);
            if (!StringUtils.isBlank(s) && s.indexOf("@Slf4j") > -1) {
                addImport = false;
            }
        }

        //保存代码
        for (int i = 0; i < sourceList.size(); i++) {
            String line = sourceList.get(i);
            if (addImport && !StringUtils.isBlank(line) && end == 0 && line.indexOf(" class ") > -1) {
                result.add("@Slf4j");
                result.add(line);
                continue;
            }
            if (addImport && i == end && end > 0) {
                result.add(line);
                result.add("@Slf4j");
                continue;
            }
            result.add(line);
        }
        return result;
    }

    /**
     * 处理导包信息
     *
     * @param sourceList
     * @return
     */
    private static List<String> handleImpoertInfo(List<String> sourceList) {
        List<String> result = new ArrayList<>();
        List<String> importLine = new ArrayList<>();
        Boolean addImport = true;
        int end = 0;
        //查出所有的导包代码
        for (int i = 0; i < sourceList.size(); i++) {
            String line = sourceList.get(i);
            if (line.indexOf("import ") > -1) {
                importLine.add(line);
                end = i;
                continue;
            }
        }

        //判断是否已经导入包
        for (int i = 0; i < importLine.size(); i++) {
            String s = importLine.get(i);
            if (!StringUtils.isBlank(s) && s.indexOf("import lombok.extern.slf4j.Slf4j;") > -1) {
                addImport = false;
            }
        }

        //保存代码
        for (int i = 0; i < sourceList.size(); i++) {
            result.add(sourceList.get(i));
            if (addImport && i == end) {
                result.add("import lombok.extern.slf4j.Slf4j;");
            }
        }
        return result;
    }

    private static String formatSource(String javaSource) {
        StringBuffer output = new StringBuffer();
        Jalopy j = new Jalopy();
        j.setEncoding("UTF-8");
        j.setInput(javaSource, "a.java");
        j.setOutput(output);
        j.format();

        return output.toString();
    }

    private static String getMethodStart(String line) {
        System.out.println("getMethodStart input line:" + line);
        if (null == line) {
            return "";
        }
        //String regEx = "\\s*[a-zA-Z0-9]+\\(.*\\)\\s*\\{";//匹配指定范围内的数字
        String regEx = "\\s*[a-zA-Z0-9]+\\(.*\\).*\\{";//匹配指定范围内的数字
        //Pattern是一个正则表达式经编译后的表现模式
        Pattern p = Pattern.compile(regEx);
        // 一个Matcher对象是一个状态机器，它依据Pattern对象做为匹配模式对字符串展开匹配检查。
        Matcher m = p.matcher(line);
        if (m.find()) {
            String reault = m.toMatchResult().group().trim();
            System.out.println("getMethodStart return :" + reault);
            return reault;
        }
        System.out.println("getMethodStart return : ");
        return "";
    }

    private static List<String> getMethodParam(String line) {
        List<String> list = new ArrayList<>();
        if (null == line) {
            return list;
        }
        //String regEx = "\\s*[a-zA-Z0-9]+\\(.*\\)\\s*\\{";//匹配指定范围内的数字
        String regEx = "\\(.*\\)";//匹配指定范围内的数字
        //Pattern是一个正则表达式经编译后的表现模式
        Pattern p = Pattern.compile(regEx);
        // 一个Matcher对象是一个状态机器，它依据Pattern对象做为匹配模式对字符串展开匹配检查。
        Matcher m = p.matcher(line);
        if (m.find()) {
            String str = m.toMatchResult().group().trim();
            str = str.substring(str.indexOf("("), str.indexOf(")"));
            if (str.indexOf(",") > -1) {
                String[] split = str.split(",");
                for (int i = 0; i < split.length; i++) {
                    String[] s = split[i].trim().split(" ");
                    list.add(s[1].trim());
                }
            } else {
                String[] s = str.split(" ");
                list.add(s[1].trim());
            }
        }
        return list;
    }


    private static String getMethodName(String line) {
        if (null == line) {
            return null;
        }
        String regEx = "([a-zA-Z0-9]+)(\\(.*\\).*\\{)";//匹配指定范围内的数字
        //Pattern是一个正则表达式经编译后的表现模式
        Pattern p = Pattern.compile(regEx);
        // 一个Matcher对象是一个状态机器，它依据Pattern对象做为匹配模式对字符串展开匹配检查。
        Matcher m = p.matcher(line);
        if (m.find()) {
            return m.toMatchResult().group(1).trim();
        }
        return null;
    }
}
